//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.example.demo.config;

import com.google.common.base.Preconditions;
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Generated;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.underlying.common.config.exception.ShardingSphereConfigurationException;
//import org.apache.shardingsphere.infra.config.exception.ShardingSphereConfigurationException;
//import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
//import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
//import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

public final class IntervalShardingAlgorithm implements StandardShardingAlgorithm<Comparable<?>> {
    private static final String DATE_TIME_PATTERN_KEY = "datetime-pattern";
    private static final String DATE_TIME_LOWER_KEY = "datetime-lower";
    private static final String DATE_TIME_UPPER_KEY = "datetime-upper";
    private static final String SHARDING_SUFFIX_FORMAT_KEY = "sharding-suffix-pattern";
    private static final String INTERVAL_AMOUNT_KEY = "datetime-interval-amount";
    private static final String INTERVAL_UNIT_KEY = "datetime-interval-unit";
    private Properties props = new Properties();
    private DateTimeFormatter dateTimeFormatter;
    private int dateTimePatternLength;
    private LocalDateTime dateTimeLower;
    private LocalDateTime dateTimeUpper;
    private DateTimeFormatter tableSuffixPattern;
    private int stepAmount;
    private ChronoUnit stepUnit;

    public IntervalShardingAlgorithm() {
    }

    public void init() {
        String dateTimePattern = this.getDateTimePattern();
        this.dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimePattern);
        this.dateTimePatternLength = dateTimePattern.length();
        this.dateTimeLower = this.getDateTimeLower(dateTimePattern);
        this.dateTimeUpper = this.getDateTimeUpper(dateTimePattern);
        this.tableSuffixPattern = this.getTableSuffixPattern();
        this.stepAmount = Integer.parseInt(this.props.getOrDefault("datetime-interval-amount", 1).toString());
        this.stepUnit = this.props.containsKey("datetime-interval-unit") ? this.getStepUnit(this.props.getProperty("datetime-interval-unit")) : ChronoUnit.DAYS;
    }

    private String getDateTimePattern() {
        Preconditions.checkArgument(this.props.containsKey("datetime-pattern"), "% can not be null.", "datetime-pattern");
        return this.props.getProperty("datetime-pattern");
    }

    private LocalDateTime getDateTimeLower(String dateTimePattern) {
        Preconditions.checkArgument(this.props.containsKey("datetime-lower"), "% can not be null.", "datetime-lower");
        return this.getDateTime("datetime-lower", this.props.getProperty("datetime-lower"), dateTimePattern);
    }

    private LocalDateTime getDateTimeUpper(String dateTimePattern) {
        return this.props.containsKey("datetime-upper") ? this.getDateTime("datetime-upper", this.props.getProperty("datetime-upper"), dateTimePattern) : LocalDateTime.now();
    }

    private LocalDateTime getDateTime(String dateTimeKey, String dateTimeValue, String dateTimePattern) {
        try {
            return LocalDateTime.parse(dateTimeValue, this.dateTimeFormatter);
        } catch (DateTimeParseException var5) {
            throw new ShardingSphereConfigurationException("Invalid %s, datetime pattern should be `%s`, value is `%s`", new Object[]{dateTimeKey, dateTimePattern, dateTimeValue});
        }
    }

    private DateTimeFormatter getTableSuffixPattern() {
        Preconditions.checkArgument(this.props.containsKey("sharding-suffix-pattern"), "%s can not be null.", "sharding-suffix-pattern");
        return DateTimeFormatter.ofPattern(this.props.getProperty("sharding-suffix-pattern"));
    }

    private ChronoUnit getStepUnit(String stepUnit) {
        ChronoUnit[] var2 = ChronoUnit.values();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            ChronoUnit each = var2[var4];
            if (each.toString().equalsIgnoreCase(stepUnit)) {
                return each;
            }
        }

        throw new UnsupportedOperationException(String.format("Cannot find step unit for specified %s property: `%s`", "datetime-interval-unit", stepUnit));
    }

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Comparable<?>> shardingValue) {
        return (String)this.doSharding(availableTargetNames, Range.singleton(shardingValue.getValue())).stream().findFirst().orElse((String)null);
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Comparable<?>> shardingValue) {
        return this.doSharding(availableTargetNames, shardingValue.getValueRange());
    }

    private Collection<String> doSharding(Collection<String> availableTargetNames, Range<Comparable<?>> range) {
        Set<String> result = new HashSet();

        for(LocalDateTime calculateTime = this.dateTimeLower; !calculateTime.isAfter(this.dateTimeUpper); calculateTime = calculateTime.plus((long)this.stepAmount, this.stepUnit)) {
            if (this.hasIntersection(Range.closedOpen(calculateTime, calculateTime.plus((long)this.stepAmount, this.stepUnit)), range)) {
                result.addAll(this.getMatchedTables(calculateTime, availableTargetNames));
            }
        }

        return result;
    }

    private boolean hasIntersection(Range<LocalDateTime> calculateRange, Range<Comparable<?>> range) {
        LocalDateTime lower = range.hasLowerBound() ? this.parseDateTime(range.lowerEndpoint().toString()) : this.dateTimeLower;
        LocalDateTime upper = range.hasUpperBound() ? this.parseDateTime(range.upperEndpoint().toString()) : this.dateTimeUpper;
        BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : BoundType.CLOSED;
        BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : BoundType.CLOSED;
        Range<LocalDateTime> dateTimeRange = Range.range(lower, lowerBoundType, upper, upperBoundType);
        return calculateRange.isConnected(dateTimeRange) && !calculateRange.intersection(dateTimeRange).isEmpty();
    }

    private LocalDateTime parseDateTime(String value) {
        return LocalDateTime.parse(value.substring(0, this.dateTimePatternLength), this.dateTimeFormatter);
    }

    private Collection<String> getMatchedTables(LocalDateTime dateTime, Collection<String> availableTargetNames) {
        String tableSuffix = dateTime.format(this.tableSuffixPattern);
        return (Collection)availableTargetNames.parallelStream().filter((each) -> {
            return each.endsWith(tableSuffix);
        }).collect(Collectors.toSet());
    }

    public String getType() {
        return "INTERVAL";
    }

    @Generated
    public Properties getProps() {
        return this.props;
    }

    @Generated
    public void setProps(Properties props) {
        this.props = props;
    }
}
